package com.JUC.cha04.sec01;

import java.util.concurrent.TimeUnit;

/**
 *  多线程锁的理解：8锁案例说明
 *  口诀： 线程  操作  资源类
 *  8锁案例说明：
 *      1. 标准访问ab两个线程，请问先打印邮件还是短信?              邮件
 *      2. sendEmail方法中执行3秒，请问先打印邮箱还是短信？        邮件
 *      3. 添加一个普通的hello方法，请问是先打印邮件还是hello？     hello
 *      4. 有两部手机，请问先打印邮件还是短信？                    短信
 *      5. 有两个静态同步方法，有1部手机，请问先打印邮件还是短信      邮件
 *      6. 有两个静态同步方法，有2部手机，请问先打印邮件还是短信      邮件
 *      7. 有1个静态同步方法，有1个普通同步方法，有1部手机，请问先打印邮件还是短信      短信
 *      8. 有1个静态同步方法，有1个普通同步方法，有2部手机，请问先打印邮件还是短信      短信
 *  笔记总结：
 *      1-2：
 *      一个对象里面如果有多个 synchronized 方法，某一时刻内，只要有一个线程调用其中一个 synchronized 方法时，其他的线程都只能等待
 *      换句话说，某一时刻内，只能由唯一的一个线程去访问这些 synchronized 方法，锁的是当前对象this，被锁定后其他的线程都不能进入到当前对象的其他的 synchronized 方法
 *
 *      3-4：
 *      加个普通的方法后发现和锁无关
 *      换成两个对象后，不是同一把锁了，情况和1、2不同
 *
 *      5-6：都换成静态同步方法后，情况变化
 *      三种 synchronized 锁的内容有一些差别：
 *      对于普通同步方法，锁的是当前对象，通常指this，具体的一部部手机，所有的普通同步方法用的都是同一把锁   ->  实例对象本身
 *      对于静态同步方法，锁的是当前类对象，如phone.class唯一的模板，无论new几个phone都会锁上
 *      对于同步方法块，锁的是 synchronized 括号内的对象
 *
 *      7-8：当一个线程试图访问同步代码时它必须得到锁，正常退出、抛出异常时必须释放锁
 *      所有的普通同步方法用的都是同一把锁   ->  实例对象本身，就是new出来的具体实例对象本身，本类this
 *      也就是说如果一个实例对象的普通方同步方法获取锁后，该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁
 *
 *      所有的静态同步方法用的都是同一把锁   ->  类对象本身，也就是我们说的唯一模板phone.class
 *      具体实例对象this和唯一模板class，这两把锁是两个不同的对象，所以静态同步方法与普通同步方法之间是不会有竞态关系的
 *      但是一旦一个静态同步方法获取锁后，其他的静态同步方法都必须等待该方法释放后才能获取锁
 *
 *
 */
class phone {   // 资源类

    public static synchronized void sendEmail(){
        // 暂停几秒钟线程
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println(Thread.currentThread().getName() + "\t-----sendEmail");
    }

    public static synchronized void sendSMS(){
        System.out.println(Thread.currentThread().getName() + "\t-----sendSMS");
    }

    public void hello(){
        System.out.println("-----hello");
    }

}

public class Lock8Demo {

    public static void main(String[] args) {

        phone phone = new phone();
        phone phone1 = new phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "a").start();

        // 暂停几秒钟线程
        try { TimeUnit.MICROSECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            phone.sendSMS();
//            phone.hello();
        }, "b").start();
        
    }

}

